package com.college.common.utils.security;

import com.alibaba.fastjson2.JSON;
import com.alibaba.fastjson2.JSONObject;
import com.college.common.response.RequestParams;

import org.apache.commons.lang3.time.DateFormatUtils;
import org.apache.commons.lang3.tuple.Pair;

import org.springframework.util.Base64Utils;

import javax.crypto.Cipher;
import javax.crypto.spec.IvParameterSpec;
import javax.crypto.spec.SecretKeySpec;
import java.io.ByteArrayOutputStream;
import java.io.UnsupportedEncodingException;
import java.nio.charset.Charset;
import java.security.*;
import java.security.interfaces.RSAPrivateKey;
import java.security.interfaces.RSAPublicKey;
import java.security.spec.PKCS8EncodedKeySpec;
import java.security.spec.X509EncodedKeySpec;
import java.util.*;

/**
 * @author xujian
 * @Date 2019/7/5
 */
public class GMsha1Util {

    private static final String encoding = "UTF-8";
    private static final String AES_CBC_PKC_ALG = "AES/CBC/PKCS5Padding";
    private static final byte[] AES_IV = initIV(AES_CBC_PKC_ALG);
    private static final String RSA_ECB_PKCS1 = "RSA/ECB/PKCS1Padding";

    public static final String prikey="MIICdgIBADANBgkqhkiG9w0BAQEFAASCAmAwggJcAgEAAoGBAJakSCsvlAj0FrSIMRDuxhnli9fYxO3NfIg08sabrUBOAsf+etQZCsHpHXpDcEG1kmT0iy3M67MQ/Ndux67jfESRofjSWllQpQ5Sk87pno/lMGkpWvoQoMzBrKOpE2i7TcL8kxQqLQCYj5tFqEhK9zyN0GzPtyrEJLJo9v6g9L3LAgMBAAECgYA6DYViXyuFZ8t4NyPxipL9V+DUhc0ed5oOQ5lrwIK7zVR1DjT3l871e4jInUSdjgOktDkOpXIPx/QocaCyXSh+C6eBCdLCCwb/DYd5DDC3qdHNooD9UxIjLsS6mUbKs3Vma3NdfV1j61Un8GGYbzh8VDUto+Y8tg0m1jY+5btrgQJBAN81uKUzAd6+Z6anwNONiSe46p8S728IdCuWNfOC8iXzczCImVOr9irlrwhDHpyxPjzwrYv1vnJVifi8jLeLPNkCQQCsxXuaBMIZqjfbdJ/ZJnnclx/yK9L2a5otueQzkEK+i5hdF5tY8rckWQsUL6KogbhzPaFU6EJFi7kRds4SA7lDAkAlbHBk4kRyACKIwp+F2LkiO9K6kQYr65Luq+Sny1CmJEcv3/c1HCPN1WyfSgGb6+uvc/rk+HLR4J2dYp+FNochAkAkGG4mU3Ap5IIe+1KaD4vOJlSqR7WffgOMIhYnH7CSFqAScuglwS+6DFBgO0OEaPWcPfgCwTN3TCWyHFwZL2X5AkEA1KGZCwMyzucE3d5ub0XUnpS+1BhQ1CNrZ1/sYuoQK6/3P2h9pPH7WqVLnZ1pJJYcjcj1BNCh4/niq9gMs3Cybg==";
    public static final String publicKey = "MIGfMA0GCSqGSIb3DQEBAQUAA4GNADCBiQKBgQCWpEgrL5QI9Ba0iDEQ7sYZ5YvX2MTtzXyINPLGm61ATgLH/nrUGQrB6R16Q3BBtZJk9IstzOuzEPzXbseu43xEkaH40lpZUKUOUpPO6Z6P5TBpKVr6EKDMwayjqRNou03C/JMUKi0AmI+bRahISvc8jdBsz7cqxCSyaPb+oPS9ywIDAQAB";
    /**
     * 加密算法RSA
     */
    public static final String KEY_ALGORITHM = "RSA";
    /**
     * 签名算法 SHA256WithRSA
     */
    public static final String SIGNATURE_ALGORITHM = "SHA256WithRSA";

    /**
     * 签名算法 SHA1WithRSA
     */
    public static final String SIGNATURE_ALGORITHM_SHA1 = "SHA1WithRSA";


//    static class RequestParams {
//        private String body;
//        private Map<String, String> headers = new HashMap<>();
//
//        public String getBody() {
//            return body;
//        }
//
//        public void setBody(String body) {
//            this.body = body;
//        }
//
//        public Map<String, String> getHeaders() {
//            return headers;
//        }
//
//        public void setHeaders(Map<String, String> headers) {
//            this.headers = headers;
//        }
//    }

    public static void main(String[] args) {
        try {

            Pair<String, String> pair = genKeyPair();

            DateFormatUtils.format(new Date(), "yyyyMMdd");
            SortedMap<String, Object> treeHeadMaps = new TreeMap<>();
            treeHeadMaps.put("channelId", "c001");
            treeHeadMaps.put("code", "c0000");
            treeHeadMaps.put("time", "150000000000");
            treeHeadMaps.put("version", "1.0");


            SortedMap<String, Object> treeBodyMaps = new TreeMap<>();
            treeBodyMaps.put("userNmae", "张三");
            treeBodyMaps.put("idNo", "000001");
            treeBodyMaps.put("bankNo", "6225677777777");
            treeBodyMaps.put("phoneNo", "13800000002");

            Map<String, Object> map = new HashMap<>();
            map.put("reqHead", JSONObject.toJSONString(treeHeadMaps));
            map.put("reqData", JSONObject.toJSONString(treeBodyMaps));

            String reqMsg = JSONObject.toJSONString(map);
            System.out.println("明文请求数据：reqMsg=" + reqMsg);
            String key = GMsha1Util.getRandom();


            //私钥
            String privateKey = pair.getKey();
            //公钥
            String publicKey1 = pair.getValue();
            System.out.println("priKey=>"+privateKey);
            System.out.println("pubKey=>"+publicKey1);

            RequestParams requestParams = encryptReqMsg(reqMsg, key, prikey, publicKey);


            System.out.println("加密加签后的请求数据：encryReqData=" + JSONObject.toJSONString(requestParams));

            //开始发起http请求
            //httpPost("http://xxxxxx",requestParams) 调用

            System.out.println("---------------开始对上面加密的数据解密和验证签名-----------------");
            System.out.println("请求内容体---------------" + requestParams.getBody());
            System.out.println("请求头---------------" + JSONObject.toJSONString(requestParams.getHeaders()));
            System.out.println("签名---------------" + requestParams.getHeaders().get("signData"));


            GMsha1Util guardUtil = new GMsha1Util();
            String decryptRspStr = new String(decrypt4Base64(requestParams.getBody(), key));
            System.out.println("解密结果为：" + decryptRspStr);


            //验签，获取响应签名值
            String sign = requestParams.getHeaders().get("signData");
//            System.out.println("sign:" + sign);

            // rspData验签
            boolean flag = verify(decryptRspStr.getBytes(Charset.forName("UTF-8")), publicKey, sign);
            System.out.println("验签结果：flag=" + flag);

        } catch (Exception e) {
            e.printStackTrace();
        }
    }

    /**
     * 验证签名
     *
     * @param data
     * @param publicKey
     * @param sign
     * @return
     * @throws Exception
     */
    public static boolean verify(byte[] data, String publicKey, String sign)
            throws Exception {
        byte[] keyBytes = Base64Utils.decode(publicKey.getBytes(encoding));
        X509EncodedKeySpec keySpec = new X509EncodedKeySpec(keyBytes);
        KeyFactory keyFactory = KeyFactory.getInstance(KEY_ALGORITHM);
        PublicKey publicK = keyFactory.generatePublic(keySpec);
        Signature signature = Signature.getInstance(SIGNATURE_ALGORITHM);
        signature.initVerify(publicK);
        signature.update(data);
        return signature.verify(Base64Utils.decode(sign.getBytes(encoding)));
    }

    /**
     * aes解密
     *
     * @param content  待解密内容
     * @param password 解密密钥
     * @return
     */
    public static String decrypt4Base64(String content, String password) {
        try {
            byte[] byteContent = Base64Utils.decode(content.getBytes(encoding));
            byte[] enCodeFormat = password.getBytes(encoding);
            SecretKeySpec key = new SecretKeySpec(enCodeFormat, "AES");
            Cipher cipher = Cipher.getInstance(AES_CBC_PKC_ALG);// 创建密码�?
            cipher.init(Cipher.DECRYPT_MODE, key, new IvParameterSpec(AES_IV));// 初始�?
            byte[] result = cipher.doFinal(byteContent);
            return new String(result, encoding); // 加密
        } catch (Exception e) {
            e.printStackTrace();
        }
        return null;
    }

    /**
     * 加密内容
     *
     * @param reqData
     * @param key
     * @param privateKey
     * @param publicKey
     * @return
     * @throws Exception
     */
    public static RequestParams encryptReqMsg(String reqData, String key, String privateKey, String publicKey) {
        try {
            // 请求报文按照默认顺序转json
            //JSONObject reqJson = JSON.parseObject(reqData, JSONReader.Feature.OrderedField);
            JSONObject reqJson = JSON.parseObject(reqData);

            RequestParams requestParams = new RequestParams();
            // 获取请求体
            JSONObject reqDataJson = reqJson.getJSONObject("reqData");
            JSONObject reqHeadJson = reqJson.getJSONObject("reqHead");

            //RSA签名：使用私钥对请求体业务字段进行加签
            String sign = sign(reqDataJson.toJSONString().getBytes(Charset.forName("UTF-8")), privateKey);

            requestParams.getHeaders().put("signData", sign);
        // AES加密请求数据，用16位随机密钥key明文加密请求体reqData
        String AESreqData = encrypt4Base64(reqDataJson.toString(), key);
        requestParams.setBody(AESreqData);
        // 使用公钥加密16位随机密钥key
        String randomKey = null;
        randomKey = encryptByPublicKey4Pkcs5(key.getBytes(Charset.forName("UTF-8")), publicKey);
        requestParams.getHeaders().put("randomKey", randomKey);
        if ( reqHeadJson != null ) {
            Set<Map.Entry<String, Object>> entries = reqHeadJson.entrySet();
            for (Map.Entry<String, Object> entry : entries) {
                requestParams.getHeaders().put(entry.getKey(), String.valueOf(entry.getValue()));
            }
        }
            return requestParams;
        } catch (Exception e) {
            throw new RuntimeException(e);
        }
        // 最终请求报文
    }

    /**
     * aes加密
     *
     * @param content  加密的内
     * @param password 加密密码
     * @return
     */
    public static String encrypt4Base64(String content, String password) {
        try {
            byte[] bytePwd = password.getBytes(encoding);
            SecretKeySpec key = new SecretKeySpec(bytePwd, "AES");
            Cipher cipher = Cipher.getInstance(AES_CBC_PKC_ALG);// 创建密码
            byte[] byteContent = content.getBytes(encoding);
            cipher.init(Cipher.ENCRYPT_MODE, key, new IvParameterSpec(AES_IV));// 初始
            byte[] result = cipher.doFinal(byteContent);
            return new String(Base64Utils.encode(result)); // 加密
        } catch (Exception e) {
        }
        return null;
    }

    /**
     * rsa解密
     *
     * @param content    加密rsaBase64
     * @param privateKey rsa私钥
     * @return
     * @throws UnsupportedEncodingException
     */
    public static String decryptByPrivateKey4Pkcs5(byte[] content, String privateKey) {
        Cipher c = null;
        byte[] decryptedData = null;
        try {
            byte[] priByte = Base64Utils.decode(privateKey.getBytes(encoding));

            PKCS8EncodedKeySpec keySpec = new PKCS8EncodedKeySpec(priByte);
            KeyFactory keyF = KeyFactory.getInstance("RSA");
            RSAPrivateKey pk = (RSAPrivateKey) keyF.generatePrivate(keySpec);

            c = Cipher.getInstance(RSA_ECB_PKCS1);
            c.init(Cipher.DECRYPT_MODE, pk);

            byte[] contentB = Base64Utils.decode(new String(content, encoding).getBytes(encoding));
            int inputLen = contentB.length;
            ByteArrayOutputStream out = new ByteArrayOutputStream();
            int offSet = 0;
            byte[] cache;
            int i = 0;
            while (inputLen - offSet > 0) {
                if (inputLen - offSet > 128) {
                    cache = c.doFinal(contentB, offSet, 128);
                } else {
                    cache = c.doFinal(contentB, offSet, inputLen - offSet);
                }
                out.write(cache, 0, cache.length);
                i++;
                offSet = i * 128;
            }
            decryptedData = out.toByteArray();
            out.close();

        } catch (Exception e) {
            return null;
        }
        try {
            return new String(decryptedData, encoding);
        } catch (UnsupportedEncodingException e) {
            throw new RuntimeException(e);
        }
    }

    /**
     * 公钥加密
     *
     * @param data      源数
     * @param publicKey 公钥(BASE64编码)
     * @return
     * @throws Exception
     */
    public static String encryptByPublicKey4Pkcs5(byte[] data, String publicKey)
            throws Exception {
        byte[] keyBytes = Base64Utils.decode(publicKey.getBytes(encoding));
        X509EncodedKeySpec x509KeySpec = new X509EncodedKeySpec(keyBytes);
        KeyFactory keyFactory = KeyFactory.getInstance("RSA");
        RSAPublicKey publicK = (RSAPublicKey) keyFactory.generatePublic(x509KeySpec);
        // 对数据加�?
        Cipher cipher = Cipher.getInstance(RSA_ECB_PKCS1);
        cipher.init(Cipher.ENCRYPT_MODE, publicK);
        int inputLen = data.length;
        ByteArrayOutputStream out = new ByteArrayOutputStream();
        int offSet = 0;
        byte[] cache;
        int i = 0;
        // 对数据分段加
        while (inputLen - offSet > 0) {
            if (inputLen - offSet > 128) {
                cache = cipher.doFinal(data, offSet, 128);
            } else {
                cache = cipher.doFinal(data, offSet, inputLen - offSet);
            }
            out.write(cache, 0, cache.length);
            i++;
            offSet = i * 128;
        }
        byte[] encryptedData = out.toByteArray();

        out.close();
        return new String(Base64Utils.encode(encryptedData));
    }

    public static String sign(byte[] data, String privateKey) throws Exception {
        byte[] keyBytes = Base64Utils.decode(privateKey.getBytes(encoding));
        PKCS8EncodedKeySpec pkcs8KeySpec = new PKCS8EncodedKeySpec(keyBytes);
        KeyFactory keyFactory = KeyFactory.getInstance(KEY_ALGORITHM);
        PrivateKey privateK = keyFactory.generatePrivate(pkcs8KeySpec);
        Signature signature = Signature.getInstance(SIGNATURE_ALGORITHM);
        signature.initSign(privateK);
        signature.update(data);
        return new String(Base64Utils.encode(signature.sign()));
    }

    /**
     * 随机数
     *
     * @return
     */
    public static String getRandom() {
        StringBuffer sb = new StringBuffer();
        Random random = new Random();
        for (int i = 0; i < 16; i++) {
            sb.append("1234567890qwertyuiopasdfghjklzxcvbnm".charAt(random
                    .nextInt("1234567890qwertyuiopasdfghjklzxcvbnm".length())));
        }
        return sb.toString();
    }

    public static Pair<String, String> genKeyPair() throws NoSuchAlgorithmException {
        // KeyPairGenerator类用于生成公钥和私钥对，基于RSA算法生成对象
        KeyPairGenerator keyPairGen = KeyPairGenerator.getInstance("RSA");
        // 初始化密钥对生成器，密钥大小为96-1024位
        keyPairGen.initialize(1024, new SecureRandom());
        // 生成一个密钥对，保存在keyPair中
        KeyPair keyPair = keyPairGen.generateKeyPair();
        RSAPrivateKey privateKey = (RSAPrivateKey) keyPair.getPrivate();   // 得到私钥
        RSAPublicKey publicKey = (RSAPublicKey) keyPair.getPublic();  // 得到公钥
        // 得到公钥字符串
        String publicKeyString = new String(Base64Utils.encode(publicKey.getEncoded()));
        // 得到私钥字符串
        String privateKeyString = new String(Base64Utils.encode((privateKey.getEncoded())));

        return Pair.of(privateKeyString, publicKeyString);
    }

    /**
     * 初始化向
     *
     * @param aesCbcPkcAlg
     * @return
     */
    private static byte[] initIV(String aesCbcPkcAlg) {
        Cipher cp;
        try {
            cp = Cipher.getInstance(aesCbcPkcAlg);
            int blockSize = cp.getBlockSize();
            byte[] iv = new byte[blockSize];
            for (int i = 0; i < blockSize; ++i) {
                iv[i] = 0;
            }
            return iv;

        } catch (Exception e) {
            int blockSize = 16;
            byte[] iv = new byte[blockSize];
            for (int i = 0; i < blockSize; ++i) {
                iv[i] = 0;
            }
            return iv;
        }
    }



}
